package cn.org.wifit.userserver.zookeeper.manager;

import io.netty.bootstrap.Bootstrap;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.socket.nio.NioSocketChannel;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.util.HashMap;
import java.util.List;

import org.apache.zookeeper.CreateMode;
import org.apache.zookeeper.ZooKeeper;
import org.apache.zookeeper.ZooDefs.Ids;

import cn.org.wifit.core.entity.xml.IPX;
import cn.org.wifit.core.entity.xml.Server;
import cn.org.wifit.core.utils.JSONUtils;
import cn.org.wifit.core.zookeeper.ServerNodeTree;
import cn.org.wifit.userserver.im.handle.IMUserToIMServerHandler;

/**
 * @author yinwenjie
 */
public class ZookeeperIMUserWriteManager {
	/**
	 * 由spring托管的一个单例对象
	 */
	private ZooKeeper zk;
	
//	private ThreadPoolTaskExecutor imServerTCPChannelPool;
//	
//	private IMServerTCPChannelRunnable imServerTCPChannelRunnable;
	
//	private static FileSystemXmlApplicationContext AP = new FileSystemXmlApplicationContext();
	
	private EventLoopGroup groupEvent;
	
	private Bootstrap client;
	
	private IMUserToIMServerHandler chl;
	
	/**
	 * 该方法根据远程zookeeper的节点情况，初始化本地xml缓存的创建，即创建ServerNodeTree内存树
	 * @return 如果初始化成功，则返回true；其他情况返回false
	 */
	@SuppressWarnings("unchecked")
	public boolean createLocalServerNodeTree() throws Exception {
		ServerNodeTree serverNodeTree = ServerNodeTree.getNewInstance();
		
		//==========================这里进行ServerNodeTree对象的初次组建（根据zookeeper上面的初次集群信息）
		//首先是IMUser
		try {
			List<String> ipxList = this.zk.getChildren("/IMUser/ipxs", false);
			for(int index = 0 ; ipxList != null && index < ipxList.size() ; index++) {
				String ipxpath = ipxList.get(index);
				
				byte[] datas=  this.zk.getData("/IMUser/ipxs" + ipxpath, false, null);
				ObjectInputStream objectInput = new ObjectInputStream(new ByteArrayInputStream(datas));
				HashMap<String, Object> dataMap = (HashMap<String, Object>)objectInput.readObject();
				objectInput.close();
				
				//获取各参数
				String ipxSequence = ipxpath.substring(ipxpath.length() - 10, ipxpath.length());
				String ip = dataMap.get("ip").toString();
				Integer port = (Integer)dataMap.get("port");
				
				serverNodeTree.AddIMUserServerNode(ipxSequence, ip, port);
			}
		} catch(Exception e) {
			e.printStackTrace(System.out);
			return false;
		}
		
		//然后是IMServer节点
		try {
			List<String> serverList = this.zk.getChildren("/IMServer", false);
			for(int index = 0 ; serverList != null && index < serverList.size() ; index++) {
				String serverPath = serverList.get(index);
				
				byte[] datas=  this.zk.getData("/IMServer/" + serverPath, false, null);
				ObjectInputStream objectInput = new ObjectInputStream(new ByteArrayInputStream(datas));
				HashMap<String, Integer> dataMap = (HashMap<String, Integer>)objectInput.readObject();
				objectInput.close();
				
				//获取各参数
				String serverSequence = serverPath.substring(serverPath.length() - 10, serverPath.length());
				Integer fromid = dataMap.get("fromid");
				Integer toid = dataMap.get("toid");
				//构造server节点
				boolean isok = serverNodeTree.AddIMServer(serverSequence, fromid, toid);
				//TODO 这里是不是应该抛出异常
				if(!isok) {
					continue;
				}
				
				List<String> ipxList = this.zk.getChildren("/IMServer/" + serverPath , false);
				for(int sum = 0 ; ipxList != null && sum < ipxList.size() ; sum++) {
					String ipxpath = ipxList.get(sum);
					
					datas=  this.zk.getData("/IMServer/" + serverPath + "/" + ipxpath , false, null);
					objectInput = new ObjectInputStream(new ByteArrayInputStream(datas));
					HashMap<String, Object> ipxdatas = (HashMap<String, Object>)objectInput.readObject();
					objectInput.close();
					
					//获取各参数
					String ipxSequence = ipxpath.substring(ipxpath.length() - 10, ipxpath.length());
					String ip = ipxdatas.get("ip").toString();
					Integer port = (Integer)ipxdatas.get("port");
					Integer userport = (Integer)ipxdatas.get("userport");
					
					//TODO 这里是不是应该抛出异常
					isok = serverNodeTree.AddIMServerIPX(serverSequence, ipxSequence, ip, port, userport);
					if(!isok) {
						break;
					}
				}
			}
		} catch(Exception e) {
			e.printStackTrace(System.out);
			return false;
		}
		
		return true;
	}
	

	/**
	 * 该方法负责建立当前IMUser和已知的IMServer之间的稳定的TCP链接。
	 * 并将这些建立好的tcp链接回话存储在IMUserTCPChannelMap中
	 * @return 如果所有链接都成功建立，则返回true；其他情况返回false
	 * @throws Exception
	 */
	@SuppressWarnings("unchecked")
	public boolean createIMServerTCPChannel() throws Exception {
		/*
		 * 因为本地的zk映射已经建立成功，所以通过ServerNodeTree读取到现在所有已启动的IMServer。
		 * 并且一次建立这些IMUser和IMServer的TCP长连接信息
		 * */
		ServerNodeTree serverNodeTree = ServerNodeTree.getNewInstance();
		String imserverjsons = serverNodeTree.GetIMServers();
		
		this.client.channel(NioSocketChannel.class).option(ChannelOption.TCP_NODELAY, true);
		this.client.group(this.groupEvent);
		this.client.handler(this.chl);
		
		List<Server> imserverlist = JSONUtils.toBean(imserverjsons, Server.class);
		for(int index = 0 ; imserverlist != null && index < imserverlist.size() ; index++) {
			Server server = imserverlist.get(index);
			String serverSequence = server.getSequence();
			
			String ipxsjsons = serverNodeTree.GetIMServers(serverSequence);
			List<IPX> ipxs = JSONUtils.toBean(ipxsjsons, IPX.class);
			
			//开始以此建立，建立完成一个后，再建立下一个
			for(int sum = 0 ; ipxs != null && sum < ipxs.size() ; sum++) {
				IPX ipx = ipxs.get(sum);
				String ip = ipx.getIp();
				Integer userport =  ipx.getUserport();
				
				//使用bean创建一个新的IMServerTCPChannelRunnable对象
//				IMServerTCPChannelRunnable channelRunnable = (IMServerTCPChannelRunnable)AP.getBean("IMServerTCPChannelRunnable");
//				IMServerTCPChannelRunnable channelRunnable = new IMServerTCPChannelRunnable();
//				this.imServerTCPChannelRunnable.setImserverIP(ip);
//				this.imServerTCPChannelRunnable.setImserverPort(userport);
//				this.imServerTCPChannelRunnable.setSessionid(serverSequence + "_" + ipxSequence);
//				
//				//开始运行
//				this.imServerTCPChannelPool.execute(this.imServerTCPChannelRunnable);
				
				//开始连接
				try {
					this.client.connect(ip ,  userport).sync();
				} catch (InterruptedException e) {
					//如果出现错误，就终止这个IMUser的启动
					e.printStackTrace(System.out);
					System.exit(1);
				}
			}
		}
		
		return true;
	}
	
	/**
	 * 在远程zookeeper集群上创建这个IMServer所代表的叶子节点,能够表述这个服务器自身特性的节点
	 * @param ip 指定的IMUser的公网IP
	 * @param port 指定的IMUser的端口
	 * @return 如果创建成功，则返回true；其他情况返回false
	 * @throws Exception 
	 */
	@SuppressWarnings("unchecked")
	public boolean createZookeeperNode(String ip , Integer port) throws Exception {
		/*
		 * 首先检查在/IMUser/ipxs下面是否存在ip和port重复的节点
		 * 如果有则抛出异常、终止启动
		 * 如果检查通过，则添加新的ipx节点
		 * */
		List<String> userIpxlist = zk.getChildren("/IMUser/ipxs", false);
		for(int index = 0 ; userIpxlist != null && index < userIpxlist.size() ; index++) {
			String ipx = userIpxlist.get(index);
			
			//获取数据
			byte[] data = this.zk.getData("/IMUser/ipxs" + ipx, false, null);
			ObjectInputStream in = new ObjectInputStream(new ByteArrayInputStream(data));
			HashMap<String, Object> map = (HashMap<String, Object>)in.readObject();
			in.close();
			
			String _ip = map.get("ip").toString();
			Integer _port = (Integer)map.get("port");
			
			if(ip.equals(_ip) && port.intValue() == _port) {
				throw new RuntimeException("在远程zk上面发现重复的IMUser节点，服务将停止，请检查配置文件！");
			}
		}
		
		//准备数据
		HashMap<String, Object> map = new HashMap<String, Object>();
		map.put("ip", ip);
		map.put("port", port);
		ByteArrayOutputStream byteout = new ByteArrayOutputStream();
		ObjectOutputStream out = new ObjectOutputStream(byteout);
		out.writeObject(map);
		byte[] objectContext = byteout.toByteArray();
		byteout.close();
		out.close();
		
		//开始添加
		this.zk.create("/IMUser/ipxs/ipx", objectContext, Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL_SEQUENTIAL);
		return true;
	}
	
	public ZooKeeper getZk() {
		return zk;
	}

	public void setZk(ZooKeeper zk) {
		this.zk = zk;
	}


	public EventLoopGroup getGroupEvent() {
		return groupEvent;
	}


	public void setGroupEvent(EventLoopGroup groupEvent) {
		this.groupEvent = groupEvent;
	}


	public Bootstrap getClient() {
		return client;
	}


	public void setClient(Bootstrap client) {
		this.client = client;
	}


	public IMUserToIMServerHandler getChl() {
		return chl;
	}


	public void setChl(IMUserToIMServerHandler chl) {
		this.chl = chl;
	}
	
//	public ThreadPoolTaskExecutor getImServerTCPChannelPool() {
//		return imServerTCPChannelPool;
//	}
//
//	public void setImServerTCPChannelPool(
//			ThreadPoolTaskExecutor imServerTCPChannelPool) {
//		this.imServerTCPChannelPool = imServerTCPChannelPool;
//	}
//
//	public IMServerTCPChannelRunnable getImServerTCPChannelRunnable() {
//		return imServerTCPChannelRunnable;
//	}
//
//	public void setImServerTCPChannelRunnable(
//			IMServerTCPChannelRunnable imServerTCPChannelRunnable) {
//		this.imServerTCPChannelRunnable = imServerTCPChannelRunnable;
//	}
}